﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Collections.ObjectModel;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Text.RegularExpressions;
using System.Windows.Markup;
using System.Windows.Media.Imaging;

namespace TreeView
{

    [TemplatePart(Name = "ElementSelectedText", Type = typeof(TextBlock)), 
    TemplatePart(Name = "ElementBackground", Type = typeof(Rectangle)), 
    TemplatePart(Name = "ElementExpand", Type = typeof(TreeViewExpander)), 
    TemplatePart(Name = "ElementCheckbox", Type = typeof(TreeViewCheckBox)), 
    TemplatePart(Name = "ElementIcon", Type = typeof(Image)), 
    TemplatePart(Name = "ElementText", Type = typeof(TextBlock))]
    public class TreeViewNode : ExtendedTextControl
    {
        // Fields
        private bool _checked;
        private int _childrenToLoad;
        private bool _editable;
        private bool _enableCheckboxes;
        private bool _expandAll;
        private List<TreeViewNode> _expandPath;
        private bool _hasChildren;
        private string _icon;
        private string _iconExpanded;
        private Thickness _padding;
        private TreeViewNode _parent;
        private bool _selected;
        private string _text;
        //RWB start
        private long _doubleClickTicks = 0;
        //RWB end 

        internal const string ElementBackgroundName = "ElementBackground";
        internal const string ElementCheckboxName = "ElementCheckbox";
        internal const string ElementExpandName = "ElementExpand";
        internal const string ElementIconName = "ElementIcon";
        internal const string ElementInputName = "ElementInput";
        internal const string ElementSelectedTextName = "ElementSelectedText";

        // Events
        public event TreeEventHandler TreeViewNodeCheckChanged;

        public event EventHandler TreeViewNodeChildTemplateApplied;

        public event EventHandler TreeViewNodeClosed;

        public event TreeEventHandler TreeViewNodeCreated;

        public event TreeEventHandler TreeViewNodeEditingFinished;

        public event EventHandler TreeViewNodeExpanded;

        public event MouseEventHandler TreeViewNodeIconMouseDown;

        public event EventHandler TreeViewNodeIconMouseEnter;

        public event EventHandler TreeViewNodeIconMouseLeave;

        public event MouseEventHandler TreeViewNodeIconMouseMove;

        public event MouseEventHandler TreeViewNodeIconMouseUp;

        public event MouseEventHandler TreeViewNodeMouseDown;

        public event EventHandler TreeViewNodeMouseLeave;

        public event MouseEventHandler TreeViewNodeMouseOver;

        public event MouseEventHandler TreeViewNodeMouseUp;

        public event EventHandler TreeViewNodeStateChange;

        public event TreeEventHandler NodeStyleChanged;

        public event EventHandler TreeViewNodeTextMouseDown;

        public event EventHandler TreeViewNodeUpdateBase;

        public event TreeEventHandler PopulateChildren;

        //RWB start
        public event TreeEventHandler TreeViewNodeDoubleClick;
        //RWB end

        // Methods
        public TreeViewNode()
        {
            this._text = string.Empty;
            this._icon = string.Empty;
            this._iconExpanded = string.Empty;
            this._padding = new Thickness(0.0, 0.0, 0.0, 0.0);
            this.TreeViewNodes = new ObservableCollection<TreeViewNode>();
            this.OverallHeight = 16.0;
            this.Margin = new Thickness(20.0, 0.0, 0.0, 0.0);
            this.AutoApplyCheckboxesToChildren = true;
            this.TreeViewNodes.CollectionChanged += new NotifyCollectionChangedEventHandler(this.Children_CollectionChanged);
            base.Width = 10.0;
            base.Height = this.OverallHeight;
            base.FontSize = 12.0;
        }

        public TreeViewNode(string id, string text, bool hasChildren, string icon)
            : this(id, text, hasChildren, icon, icon)
        {
        }

        public TreeViewNode(string id, string text, bool hasChildren, string icon, string iconExpanded)
            : this(id, text, hasChildren, icon, icon, false)
        {
        }

        public TreeViewNode(string id, string text, bool hasChildren, string icon, string iconExpanded, bool container)
            : this()
        {
            base.ID = id;
            this.Text = text;
            this.HasChildren = hasChildren;
            this.Icon = icon;
            this.IconExpanded = iconExpanded;
            this.IsContainer = container;
        }

        public void Add(TreeViewNode node, bool expand)
        {
            if (node != null)
            {
                this.TreeViewNodes.Add(node);
                node.ParentNode = this;
                base.Canvas.Children.Add(node);
                if (expand)
                {
                    this.Expand();
                }
                else
                {
                    if (!this.Expanded)
                    {
                        this.SetChildVisibility(false);
                    }
                    this.CalculateSize();
                }
                if (!this.HasChildren)
                {
                    this.HasChildren = true;
                }
            }
        }

        public void AddPosition(Point position)
        {
            this.AddPosition(position.X, position.Y);
        }

        public void AddPosition(double x, double y)
        {
            base.SetValue(Canvas.LeftProperty, ((double)this.GetValue(Canvas.LeftProperty)) + x);
            base.SetValue(Canvas.TopProperty, ((double)this.GetValue(Canvas.TopProperty)) + y);
        }

        public void BulkUpdateBegin()
        {
            this.DisableChildUpdates();
        }

        public void BulkUpdateEnd()
        {
            this.EnableChildUpdates();
            this.SyncChildren();
        }

        public void CalculateSize()
        {
            double overallHeight = this.OverallHeight;
            double width = base.Width;
            if (base.ElementRoot != null)
            {
                if (this.Expanded)
                {
                    foreach (UIElement element in base.Canvas.Children)
                    {
                        if (element is TreeViewNode)
                        {
                            TreeViewNode node = (TreeViewNode)element;
                            node.SetValue(Canvas.TopProperty, overallHeight);
                            node.SetValue(Canvas.LeftProperty, this.Margin.Left);
                            overallHeight += node.Height;
                            double num3 = node.Width + this.Margin.Left;
                            if (num3 > width)
                            {
                                width = num3;
                            }
                        }
                    }
                }
                base.ElementRoot.Width = width;
                base.ElementRoot.Height = overallHeight;
            }
            base.Width = width;
            base.Height = overallHeight;
        }

        private void Checkbox_Clicked(object sender, RoutedEventArgs e)
        {
            this._checked = this.ElementCheckbox.IsChecked.Value;
            this.CheckChanged();
        }

        private void CheckChanged()
        {
            TreeViewEventArgs args = new TreeViewEventArgs(base.ID);
            args.Source = this;
            if ((this.Base != null) && this.Base.ApplyCheckChangesToChildren)
            {
                foreach (TreeViewNode node in this.TreeViewNodes)
                {
                    node.Checked = this._checked;
                }
            }
            this.RaiseNodeCheckChanged(this, args);
        }

        private void Children_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            this.SyncChildren();
        }

        public void Collapse()
        {
            this.Expanded = false;
            if (this.HasChildren)
            {
                this.SetChildVisibility(false);
                base.Canvas.SetValue(FrameworkElement.HeightProperty, this.OverallHeight);
                base.SetValue(FrameworkElement.HeightProperty, base.Canvas.Height);
                this.RaiseNodeClosed(this, new EventArgs());
                this.RaiseNodeStateChange(this, new EventArgs());
            }
            this.RecursiveUpdateSize();
            this.UpdateNodeIcon();
            this.UpdateVisualState();
            this.RaiseNodeUpdateBase(this, EventArgs.Empty);
        }

        public void CollapseAll()
        {
            this.Collapse();
            foreach (TreeViewNode node in this.TreeViewNodes)
            {
                node.CollapseAll();
            }
        }

        public void Delete()
        {
            if (this.ParentNode != null)
            {
                this.ParentNode.TreeViewNodes.Remove(this);
            }
        }

        public void DeleteChild(string id)
        {
            int index = this.IndexOfChild(id);
            if (index >= 0)
            {
                this.TreeViewNodes.RemoveAt(index);
                this.UpdateChildCount();
            }
        }

        public void DisableChildUpdates()
        {
            this.TreeViewNodes.CollectionChanged -= new NotifyCollectionChangedEventHandler(this.Children_CollectionChanged);
        }

        private void DocumentIcon_OnMouseDown(object sender, MouseButtonEventArgs args)
        {
            this.RaiseNodeIconMouseDown(this, args);
            this.RaiseNodeMouseDown(this, args);
        }

        private void DocumentIcon_OnMouseEnter(object sender, MouseEventArgs args)
        {
            this.RaiseNodeIconMouseEnter(this, args);
        }

        private void DocumentIcon_OnMouseLeave(object sender, MouseEventArgs args)
        {
            this.RaiseNodeIconMouseLeave(this, args);
        }

        private void DocumentIcon_OnMouseMove(object sender, MouseEventArgs args)
        {
            this.RaiseNodeIconMouseMove(this, args);
        }

        private void DocumentIcon_OnMouseUp(object sender, MouseButtonEventArgs args)
        {
            this.RaiseNodeIconMouseUp(this, args);
            this.RaiseNodeMouseUp(this, args);
        }

        public void EnableChildUpdates()
        {
            this.TreeViewNodes.CollectionChanged -= new NotifyCollectionChangedEventHandler(this.Children_CollectionChanged);
            this.TreeViewNodes.CollectionChanged += new NotifyCollectionChangedEventHandler(this.Children_CollectionChanged);
        }

        public void EnableNodeEvents()
        {
            if (base.ElementRoot != null)
            {
                if (this.ElementCheckbox != null)
                {
                    this.ElementCheckbox.Click += new RoutedEventHandler(this.Checkbox_Clicked);
                }
                this.ElementIcon.MouseEnter += new MouseEventHandler(this.DocumentIcon_OnMouseEnter);
                this.ElementIcon.MouseLeave += new MouseEventHandler(this.DocumentIcon_OnMouseLeave);
                this.ElementIcon.MouseLeftButtonDown += new MouseButtonEventHandler(this.DocumentIcon_OnMouseDown);
                this.ElementIcon.MouseLeftButtonUp += new MouseButtonEventHandler(this.DocumentIcon_OnMouseUp);
                this.ElementIcon.MouseMove += new MouseEventHandler(this.DocumentIcon_OnMouseMove);
                base.ElementText.MouseLeftButtonDown += new MouseButtonEventHandler(this.TextText_OnMouseDown);
                base.ElementText.MouseEnter += new MouseEventHandler(this.TextText_MouseEnter);
                base.ElementText.MouseLeave += new MouseEventHandler(this.TextText_MouseLeave);
                //RWB start
                this.ElementSelectedText.MouseLeftButtonDown += new MouseButtonEventHandler(this.TextText_OnMouseDown);
                //RWB end
            }
        }

        public void Expand()
        {
            this.Expanded = true;
            if (this.HasChildren)
            {
                if (!this.ChildrenLoaded)
                {
                    this.Populate(base.Canvas);
                }
                else
                {
                    this.RecursiveUpdateSize();
                    this.RaiseNodeUpdateBase(this, EventArgs.Empty);
                }
                this.SetChildVisibility(true);
                this.RaiseNodeExpanded(this, new EventArgs());
                this.RaiseNodeStateChange(this, new EventArgs());
            }
            this.UpdateNodeIcon();
            this.UpdateVisualState();
        }

        public void ExpandAll()
        {
            this.ExpandAll(null);
        }

        public void ExpandAll(List<TreeViewNode> path)
        {
            this._expandAll = true;
            this._expandPath = path;
            if (base.ElementRoot != null)
            {
                this.Expand();
                foreach (TreeViewNode node in this.TreeViewNodes)
                {
                    if ((this._expandPath == null) || this._expandPath.Contains(node))
                    {
                        node.ExpandAll(this._expandPath);
                    }
                }
            }
        }

        public void ExpandOut()
        {
            TreeViewNode parentNode = this.ParentNode;
            List<TreeViewNode> path = new List<TreeViewNode>();
            while (parentNode != null)
            {
                path.Add(parentNode);
                parentNode = parentNode.ParentNode;
            }
            if (path.Count > 0)
            {
                path.Reverse();
                path[0].ExpandAll(path);
            }
        }

        public List<TreeViewNode> Find(string terms)
        {
            List<TreeViewNode> list = new List<TreeViewNode>();
            if (Regex.Match(this._text, terms, RegexOptions.IgnoreCase).Success)
            {
                list.Add(this);
            }
            foreach (TreeViewNode node in this.TreeViewNodes)
            {
                list.AddRange(node.Find(terms));
            }
            return list;
        }

        public TreeViewNode Get(string id)
        {
            TreeViewNode node = null;

            if (string.Compare(this.ID, id, StringComparison.CurrentCulture) == 0)
                return this;

            foreach (TreeViewNode node2 in this.TreeViewNodes)
            {
                node = node2.Get(id);
                if (node != null)
                {
                    return node;
                }
            }
            return node;
        }

        public TreeViewNode GetChild(int index)
        {
            TreeViewNode node = null;
            if (index >= 0)
            {
                node = this.TreeViewNodes[index];
            }
            return node;
        }

        public TreeViewNode GetChild(string id)
        {
            return this.GetChild(this.IndexOfChild(id));
        }

        public int IndexOfChild(string id)
        {
            for (int i = 0; i < this.TreeViewNodes.Count; i++)
            {
                if (this.TreeViewNodes[i].ID == id)
                {
                    return i;
                }
            }
            return -1;
        }

        //RWB
        public string GetSetSTring()
        {
            if (ParentNode == null)
                return Convert.ToChar(Base.TreeViewNodes.IndexOf(this) + 65).ToString();
            return ParentNode.GetSetSTring() + Convert.ToChar(ParentNode.IndexOfChild(ID) + 65);
        }

        public int IndexOfNext()
        {
            int num = -1;
            if (this.ParentNode != null)
            {
                int index = this.ParentNode.TreeViewNodes.IndexOf(this);
                if (index < (this.ParentNode.TreeViewNodes.Count - 1))
                {
                    num = index + 1;
                }
            }
            //RWB start - this might be a breaking change for your code
            else  //this is root element
            { 
                //use the tree to get the index of this
                int index = Base.TreeViewNodes.IndexOf(this);
                //if it isn't the last one
                if (index != Base.TreeViewNodes.Count - 1)
                    num = index + 1;
            }
            //RWB end
            return num;
        }

        public int IndexOfPrevious()
        {
            int num = -1;
            if (this.ParentNode != null)
            {
                int index = this.ParentNode.TreeViewNodes.IndexOf(this);
                if (index > 0)
                {
                    num = index - 1;
                }
            }
            //RWB start - this might be a breaking change for your code
            else  //this is root element
            {
                //use the tree to get the index of this
                int index = Base.TreeViewNodes.IndexOf(this);
                //if it isn't the first one
                if (index != 0)
                    num = index - 1;
            }
            //RWB end
            return num;
        }

        public TreeViewNode Next()
        {
            if (this.ParentNode == null)
            {
                return null;
            }
            return this.ParentNode.GetChild(this.IndexOfNext());
        }
        //RWB start Added Extended Next Functionality This function returns the next node in the collection.  
        //if the node is at the bottom of a nodes collection it checks for a node following the parent
        public TreeViewNode NextEx()
        {
            //if we have children and we are expanded then we want to go to the first child
            if (HasChildren && Expanded)
            {
                if (TreeViewNodes.Count > 0)
                    return TreeViewNodes[0];
                else
                    return null;
            }

            int index = this.IndexOfNext();
            //if there is not a next node
            if (index == -1)
            {
                TreeViewNode temp = this;
                //if there is not a parent to check
                while(temp.ParentNode != null)
                {
                    //Get the index of the next node
                    int indexParent = temp.ParentNode.IndexOfNext();

                    //if there is not a next node beyond the parent
                    if (indexParent != -1)                    
                    {
                        //if we are at the top of the tree then use the tree otherwise use the parent-parent node
                        if (temp.ParentNode.ParentNode != null)
                            return temp.ParentNode.ParentNode.GetChild(indexParent);
                        else
                            return Base.TreeViewNodes[indexParent];
                    }

                    temp = temp.ParentNode;
                }
                //We have exhausted all of the parents
                return null;
            }
            //There is a next node so return it
            if(this.ParentNode == null)
                return this.Base.TreeViewNodes[index];
            else
                return this.ParentNode.GetChild(index);
        }
        //RWB end

        private void Node_NodeChildTemplateApplied(object sender, EventArgs e)
        {
            this._childrenToLoad--;
            if (this._childrenToLoad <= 0)
            {
                this._childrenToLoad = 0;
                this.RecursiveUpdateSize();
                this.RaiseNodeUpdateBase(this, EventArgs.Empty);
                if (this._expandAll)
                {
                    this._expandAll = false;
                    foreach (TreeViewNode node in this.TreeViewNodes)
                    {
                        if ((this._expandPath == null) || this._expandPath.Contains(node))
                        {
                            node.ExpandAll(this._expandPath);
                        }
                    }
                }
            }
        }

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
            this.ElementBackground = (Rectangle)base.GetTemplateChild("ElementBackground");
            this.ElementIcon = (Image)base.GetTemplateChild("ElementIcon");
            this.ElementSelectedText = (TextBlock)base.GetTemplateChild("ElementSelectedText");
            this.ElementExpand = (TreeViewExpander)base.GetTemplateChild("ElementExpand");
            this.ElementInput = (TextBox)base.GetTemplateChild("ElementInput");
            this.ElementExpand.Click += new EventHandler(this.PrefixIcon_OnMouseDown);
            this.EnableNodeEvents();
            this.UpdateCheckbox();
            this.TextStyleChanged();
            base.TemplateApplied = true;
            this.RaiseNodeChildTemplateApplied(this, EventArgs.Empty);
            if (this.Expanded)
            {
                this.SyncChildren();
                this.UpdateNodeIcon();
                this.UpdateVisualState();
            }
        }

        private void ParentChanged()
        {
            if (this.ParentNode != null)
            {
                this.TreeViewNodeStateChange = this.ParentNode.TreeViewNodeStateChange;
                this.TreeViewNodeClosed = this.ParentNode.TreeViewNodeClosed;
                this.TreeViewNodeExpanded = this.ParentNode.TreeViewNodeExpanded;
                this.TreeViewNodeIconMouseEnter = this.ParentNode.TreeViewNodeIconMouseEnter;
                this.TreeViewNodeIconMouseLeave = this.ParentNode.TreeViewNodeIconMouseLeave;
                this.TreeViewNodeIconMouseDown = this.ParentNode.TreeViewNodeIconMouseDown;
                this.TreeViewNodeIconMouseUp = this.ParentNode.TreeViewNodeIconMouseUp;
                this.TreeViewNodeIconMouseMove = this.ParentNode.TreeViewNodeIconMouseMove;
                this.TreeViewNodeTextMouseDown = this.ParentNode.TreeViewNodeTextMouseDown;
                this.TreeViewNodeMouseLeave = this.ParentNode.TreeViewNodeMouseLeave;
                this.TreeViewNodeMouseOver = this.ParentNode.TreeViewNodeMouseOver;
                this.TreeViewNodeMouseDown = this.ParentNode.TreeViewNodeMouseDown;
                this.TreeViewNodeMouseUp = this.ParentNode.TreeViewNodeMouseUp;
                this.PopulateChildren = this.ParentNode.PopulateChildren;
                this.TreeViewNodeCreated = this.ParentNode.TreeViewNodeCreated;
                this.TreeViewNodeCheckChanged = this.ParentNode.TreeViewNodeCheckChanged;
                this.TreeViewNodeUpdateBase = this.ParentNode.TreeViewNodeUpdateBase;
                if (this.ParentNode.AutoApplyCheckboxesToChildren)
                {
                    this.EnableCheckboxes = this.ParentNode.EnableCheckboxes;
                }
                //RWB start
                this.TreeViewNodeDoubleClick = this.ParentNode.TreeViewNodeDoubleClick;
                //RWB end
            }
        }

        private void Populate(Canvas canvas)
        {
            TreeViewEventArgs args = new TreeViewEventArgs(base.ID);
            this.RaisePopulateChildren(this, args);
            this.SyncChildren();
            this.ChildrenLoaded = true;
        }

        private void PrefixIcon_OnMouseDown(object sender, EventArgs args)
        {
            if (!this.Expanded)
            {
                this.Expand();
            }
            else
            {
                this.Collapse();
            }
        }

        public TreeViewNode Previous()
        {
            if (this.ParentNode == null)
            {
                return null;
            }
            return this.ParentNode.GetChild(this.IndexOfPrevious());
        }

        //RWB start Added Extended Previous functionality.  This function returns the previous visible node.          
        public TreeViewNode PreviousEx()
        {
            int index = this.IndexOfPrevious();
            bool hasParent = ParentNode != null;
            TreeViewNode temp;
            //if there is not a previous node
            if (index == -1)
            {
                //if there is a parent then send it back
                if (hasParent)
                    return ParentNode;
                else  //if there is not a parent to check then we are at the top of the tree
                    return null;
            }
            else  //There is a previous node so return it
            {
                if(hasParent)
                    temp = ParentNode.TreeViewNodes[index];
                else  //This node has no parent and we are at the top of the tree
                    temp = this.Base.TreeViewNodes[index];
            }

            //Now that we have the node to check then find the lowest opened node
            while(temp.HasChildren && temp.Expanded)
                temp = temp.TreeViewNodes[temp.TreeViewNodes.Count -1];

            return temp;
        }
        //RWB end

        private void RaiseNodeCheckChanged(object sender, TreeViewEventArgs args)
        {
            if (this.TreeViewNodeCheckChanged != null)
            {
                this.TreeViewNodeCheckChanged(sender, args);
            }
        }

        private void RaiseNodeChildTemplateApplied(object sender, EventArgs args)
        {
            if (this.TreeViewNodeChildTemplateApplied != null)
            {
                this.TreeViewNodeChildTemplateApplied(sender, args);
            }
        }

        private void RaiseNodeClosed(object sender, EventArgs args)
        {
            if (this.TreeViewNodeClosed != null)
            {
                this.TreeViewNodeClosed(sender, args);
            }
        }

        private void RaiseNodeCreated(object sender, TreeViewEventArgs args)
        {
            if (this.TreeViewNodeCreated != null)
            {
                this.TreeViewNodeCreated(sender, args);
            }
        }

        private void RaiseNodeEditingFinished(object sender, TreeViewEventArgs args)
        {
            if (this.TreeViewNodeEditingFinished != null)
            {
                this.TreeViewNodeEditingFinished(sender, args);
            }
        }

        private void RaiseNodeExpanded(object sender, EventArgs args)
        {
            if (this.TreeViewNodeExpanded != null)
            {
                this.TreeViewNodeExpanded(sender, args);
            }
        }

        private void RaiseNodeIconMouseDown(object sender, MouseEventArgs args)
        {
            if (this.TreeViewNodeIconMouseDown != null)
            {
                this.TreeViewNodeIconMouseDown(sender, args);
            }
        }

        private void RaiseNodeIconMouseEnter(object sender, EventArgs args)
        {
            if (this.TreeViewNodeIconMouseEnter != null)
            {
                this.TreeViewNodeIconMouseEnter(sender, args);
            }
        }

        private void RaiseNodeIconMouseLeave(object sender, EventArgs args)
        {
            if (this.TreeViewNodeIconMouseLeave != null)
            {
                this.TreeViewNodeIconMouseLeave(sender, args);
            }
        }

        private void RaiseNodeIconMouseMove(object sender, MouseEventArgs args)
        {
            if (this.TreeViewNodeIconMouseMove != null)
            {
                this.TreeViewNodeIconMouseMove(sender, args);
            }
        }

        private void RaiseNodeIconMouseUp(object sender, MouseEventArgs args)
        {
            if (this.TreeViewNodeIconMouseUp != null)
            {
                this.TreeViewNodeIconMouseUp(sender, args);
            }
        }

        private void RaiseNodeMouseDown(object sender, MouseEventArgs args)
        {
            if (this.TreeViewNodeMouseDown != null)
            {
                this.TreeViewNodeMouseDown(sender, args);
            }
        }

        private void RaiseNodeMouseLeave(object sender, EventArgs args)
        {
            if (this.TreeViewNodeMouseLeave != null)
            {
                this.TreeViewNodeMouseLeave(sender, args);
            }
        }

        private void RaiseNodeMouseOver(object sender, MouseEventArgs args)
        {
            if (this.TreeViewNodeMouseOver != null)
            {
                this.TreeViewNodeMouseOver(sender, args);
            }
        }

        private void RaiseNodeMouseUp(object sender, MouseEventArgs args)
        {
            if (this.TreeViewNodeMouseUp != null)
            {
                this.TreeViewNodeMouseUp(sender, args);
            }
        }

        private void RaiseNodeStateChange(object sender, EventArgs args)
        {
            if (this.TreeViewNodeStateChange != null)
            {
                this.TreeViewNodeStateChange(sender, args);
            }
        }

        private void RaiseNodeStyleChanged(object sender, TreeViewEventArgs args)
        {
            if (this.NodeStyleChanged != null)
            {
                this.NodeStyleChanged(sender, args);
            }
        }

        private void RaiseNodeTextMouseDown(object sender, EventArgs args)
        {
            if (this.TreeViewNodeTextMouseDown != null)
            {
                this.TreeViewNodeTextMouseDown(sender, args);
            }
        }

        private void RaiseNodeUpdateBase(object sender, EventArgs args)
        {
            if (this.TreeViewNodeUpdateBase != null)
            {
                this.TreeViewNodeUpdateBase(sender, args);
            }
        }

        private void RaisePopulateChildren(object sender, TreeViewEventArgs args)
        {
            if (this.PopulateChildren != null)
            {
                this.PopulateChildren(sender, args);
            }
        }

        private void RaiseDoubleClick(object sender, TreeViewEventArgs args)
        {
            if (this.TreeViewNodeDoubleClick != null)
                this.TreeViewNodeDoubleClick(sender, args);
        }

        public void RecursiveUpdateSize()
        {
            for (TreeViewNode node = this; node != null; node = node.ParentNode)
            {
                node.CalculateSize();
            }
        }

        public void Refresh()
        {
            if (this.ChildrenLoaded)
            {
                this.TreeViewNodes.Clear();
                this.Populate(base.Canvas);
                this.HasChildren = this.TreeViewNodes.Count > 0;
                this.SetChildVisibility(this.Expanded);
                this.CalculateSize();
                this.UpdateNodeIcon();
                this.UpdateVisualState();
            }
        }

        public void SelectChild(string id)
        {
            int num = this.IndexOfChild(id);
            if (num >= 0)
            {
                this.RaiseNodeTextMouseDown(this.TreeViewNodes[num], null);
            }
        }

        public void SetBackground(string xaml, double x, double y, double width, double height)
        {
            if (xaml.Length > 0)
            {
                Brush brush = (Brush)XamlReader.Load(xaml);
                this.ElementBackground.Fill = brush;
                this.SetBackingSize(x, y, width, height);
                this.ElementBackground.Visibility = Visibility.Visible;
            }
        }

        private void SetBackingSize(double x, double y, double width, double height)
        {
            this.ElementBackground.SetValue(Canvas.TopProperty, y);
            this.ElementBackground.SetValue(Canvas.LeftProperty, x - 2.0);
            this.ElementBackground.SetValue(FrameworkElement.WidthProperty, width + 4.0);
            this.ElementBackground.SetValue(FrameworkElement.HeightProperty, height);
        }

        private void SetChildVisibility(bool visible)
        {
            foreach (TreeViewNode node in this.TreeViewNodes)
            {
                node.Visibility = visible ? Visibility.Visible : Visibility.Collapsed;
            }
        }

        public void SetOpacity(double opacity)
        {
            if (base.ElementRoot != null)
            {
                this.ElementBackground.Opacity = opacity;
                this.ElementExpand.Opacity = opacity;
                if (this.ElementCheckbox != null)
                {
                    this.ElementCheckbox.Opacity = opacity;
                }
                this.ElementIcon.Opacity = opacity;
                base.ElementText.Opacity = opacity;
                this.ElementSelectedText.Opacity = opacity;
                foreach (TreeViewNode node in this.TreeViewNodes)
                {
                    node.SetOpacity(opacity);
                }
            }
        }

        public void Sort(TreeView.SortActions sort)
        {
            bool sortAll = (sort & TreeView.SortActions.ApplyToChildren) != TreeView.SortActions.Default;
            if ((sort & TreeView.SortActions.ContainersFirst) != TreeView.SortActions.Default)
            {
                if ((sort & TreeView.SortActions.Descending) != TreeView.SortActions.Default)
                {
                    this.Sort(sortAll, new Comparison<TreeViewNode>(TreeViewNode.SortReverseContainersFirstCallback));
                }
                else
                {
                    this.Sort(sortAll, new Comparison<TreeViewNode>(TreeViewNode.SortContainersFirstCallback));
                }
            }
            else if ((sort & TreeView.SortActions.Descending) != TreeView.SortActions.Default)
            {
                this.Sort(sortAll, new Comparison<TreeViewNode>(TreeViewNode.SortReverseCallback));
            }
            else
            {
                this.Sort(sortAll, new Comparison<TreeViewNode>(TreeViewNode.SortCallback));
            }
        }

        public void Sort(bool sortAll, Comparison<TreeViewNode> comparer)
        {
            List<TreeViewNode> list = new List<TreeViewNode>();
            if (this.TreeViewNodes.Count > 0)
            {
                list.AddRange(this.TreeViewNodes);
                list.Sort(comparer);
                this.BulkUpdateBegin();
                this.TreeViewNodes.Clear();
                foreach (TreeViewNode node in list)
                {
                    this.TreeViewNodes.Add(node);
                    if (sortAll)
                    {
                        node.Sort(sortAll, comparer);
                    }
                }
                this.BulkUpdateEnd();
            }
        }

        public static int SortCallback(TreeViewNode a, TreeViewNode b)
        {
            return a.Text.CompareTo(b.Text);
        }

        public static int SortContainersFirstCallback(TreeViewNode a, TreeViewNode b)
        {
            string str = a.IsContainer ? "0" : "1";
            string str2 = b.IsContainer ? "0" : "1";
            return (str + a.Text).CompareTo(str2 + b.Text);
        }

        public static int SortReverseCallback(TreeViewNode a, TreeViewNode b)
        {
            return b.Text.CompareTo(a.Text);
        }

        public static int SortReverseContainersFirstCallback(TreeViewNode a, TreeViewNode b)
        {
            string str = a.IsContainer ? "0" : "1";
            string str2 = b.IsContainer ? "0" : "1";
            return (str2 + b.Text).CompareTo(str + a.Text);
        }

        public void SwapNext()
        {
            ObservableCollection<TreeViewNode> observables = (this.ParentNode != null) ? this.ParentNode.TreeViewNodes : this.Base.TreeViewNodes;
            Canvas canvas = (this.ParentNode != null) ? this.ParentNode.Canvas : this.Base.ElementChildren;
            int index = observables.IndexOf(this);
            int num2 = canvas.Children.IndexOf(this);
            if (index < (observables.Count - 1))
            {
                if (this.ParentNode != null)
                {
                    this.ParentNode.DisableChildUpdates();
                }
                else
                {
                    this.Base.BulkUpdateBegin();
                }
                observables.RemoveAt(index);
                observables.Insert(index + 1, this);
                canvas.Children.RemoveAt(num2);
                canvas.Children.Insert(num2 + 1, this);
                if (this.ParentNode != null)
                {
                    this.ParentNode.EnableChildUpdates();
                    this.ParentNode.RecursiveUpdateSize();
                }
                else
                {
                    this.Base.BulkUpdateEnd();
                }
            }
        }

        public void SwapPrevious()
        {
            ObservableCollection<TreeViewNode> observables = (this.ParentNode != null) ? this.ParentNode.TreeViewNodes : this.Base.TreeViewNodes;
            Canvas canvas = (this.ParentNode != null) ? this.ParentNode.Canvas : this.Base.ElementChildren;
            int index = observables.IndexOf(this);
            int num2 = canvas.Children.IndexOf(this);
            if (index > 0)
            {
                if (this.ParentNode != null)
                {
                    this.ParentNode.DisableChildUpdates();
                }
                else
                {
                    this.Base.BulkUpdateBegin();
                }
                observables.RemoveAt(index);
                observables.Insert(index - 1, this);
                canvas.Children.RemoveAt(num2);
                canvas.Children.Insert(num2 - 1, this);
                if (this.ParentNode != null)
                {
                    this.ParentNode.EnableChildUpdates();
                    this.ParentNode.RecursiveUpdateSize();
                }
                else
                {
                    this.Base.BulkUpdateEnd();
                }
            }
        }

        public void SyncChildren()
        {
            if (base.ElementRoot != null)
            {
                int num;
                for (num = base.Canvas.Children.Count - 1; num >= 0; num--)
                {
                    if (base.Canvas.Children[num] is TreeViewNode)
                    {
                        base.Canvas.Children.RemoveAt(num);
                    }
                }
                this._childrenToLoad = 0;
                for (num = 0; num < this.TreeViewNodes.Count; num++)
                {
                    if (!this.TreeViewNodes[num].TemplateApplied)
                    {
                        TreeViewNode local1 = this.TreeViewNodes[num];
                        local1.TreeViewNodeChildTemplateApplied = (EventHandler)Delegate.Remove(local1.TreeViewNodeChildTemplateApplied, new EventHandler(this.Node_NodeChildTemplateApplied));
                        TreeViewNode local2 = this.TreeViewNodes[num];
                        local2.TreeViewNodeChildTemplateApplied = (EventHandler)Delegate.Combine(local2.TreeViewNodeChildTemplateApplied, new EventHandler(this.Node_NodeChildTemplateApplied));
                        this._childrenToLoad++;
                    }
                    if (this.TreeViewNodes[num].ParentNode != this)
                    {
                        this.TreeViewNodes[num].ParentNode = this;
                        if ((this.Base != null) && this.Base.ApplyCheckChangesToChildren)
                        {
                            this.Checked = this.TreeViewNodes[num].ParentNode.Checked;
                        }
                    }
                    this.TreeViewNodes[num].Base = this.Base;
                    this.TreeViewNodes[num].Visibility = this.Expanded ? Visibility.Visible : Visibility.Collapsed;
                    base.Canvas.Children.Add(this.TreeViewNodes[num]);
                }
                if (!this.HasChildren && (this.TreeViewNodes.Count > 0))
                {
                    this.HasChildren = true;
                }
                else if (this.TreeViewNodes.Count == 0)
                {
                    this.HasChildren = false;
                }
                if (this._childrenToLoad == 0)
                {
                    this.RecursiveUpdateSize();
                    this.RaiseNodeUpdateBase(this, EventArgs.Empty);
                }
                else
                {
                    this.CalculateSize();
                }
            }
            this.UpdateVisualState();
        }

        protected override void TextStyleChanged()
        {
            base.TextStyleChanged();
            if (base.ElementRoot != null)
            {
                this.ElementSelectedText.FontFamily = base.FontFamily;
                this.ElementSelectedText.FontSize = base.FontSize;
                this.ElementSelectedText.FontWeight = base.FontWeight;
                this.ElementSelectedText.FontStyle = base.FontStyle;
                this.ElementInput.FontFamily = base.FontFamily;
                this.ElementInput.FontSize = base.FontSize;
            }
        }

        private void TextText_MouseEnter(object sender, MouseEventArgs args)
        {
            this.RaiseNodeMouseOver(this, args);
        }

        private void TextText_MouseLeave(object sender, EventArgs args)
        {
            this.RaiseNodeMouseLeave(this, args);
        }

        private void TextText_OnMouseDown(object sender, MouseButtonEventArgs args)
        {
            //RWB start
            //This originally was just called during the RaiseNodeTextMouseDown.  In order to have the double-click 
            //event as well as the multiple select event I had to send the selectedText mouse down button event here 
            //as well.  This is a possible breaking change as the TreeNodeClick event is fired for each time the node
            //is clicked, NOT just the first time is it selected.
            
            this.RaiseNodeTextMouseDown(this, args);
            
            if ((DateTime.Now.Ticks - _doubleClickTicks) < 3100000)
            {
                TreeViewEventArgs e = new TreeViewEventArgs(base.ID);
                // double click event
                RaiseDoubleClick(sender, e);
            }
            _doubleClickTicks = DateTime.Now.Ticks;
            //RWB end
        }

        private void UpdateCheckbox()
        {
            if (base.ElementRoot != null)
            {
                if (this._enableCheckboxes)
                {
                    if (this.ElementCheckbox == null)
                    {
                        this.ElementCheckbox = new TreeViewCheckBox();
                        this.ElementCheckbox.Cursor = Cursors.Hand;
                        this.ElementCheckbox.Click += new RoutedEventHandler(this.Checkbox_Clicked);
                        base.Canvas.Children.Add(this.ElementCheckbox);
                    }
                }
                else if (this.ElementCheckbox != null)
                {
                    base.Canvas.Children.Remove(this.ElementCheckbox);
                    this.ElementCheckbox = null;
                }
                this.UpdateVisualState();
            }
        }

        public void UpdateChildCount()
        {
            this.HasChildren = this.TreeViewNodes.Count > 0;
            if (!this.HasChildren && this.Expanded)
            {
                this.Collapse();
            }
        }

        private void UpdateNodeIcon()
        {
            string uriString = (this.Expanded && (this._iconExpanded.Length > 0)) ? this._iconExpanded : this._icon;
            if ((base.ElementRoot != null) && (uriString.Length > 0))
            {
                BitmapImage image = new BitmapImage();
                image.UriSource = new Uri(uriString, UriKind.Relative);
                this.ElementIcon.Source = image;
            }
        }

        public void UpdateVisuals()
        {
            this.UpdateVisualState();
        }

        protected override void UpdateVisualState()
        {
            if (base.ElementRoot != null)
            {
                double num2 = this._padding.Left + 14.0;
                this.ElementExpand.Visibility = this.HasChildren ? Visibility.Visible : Visibility.Collapsed;
                if (this.HasChildren)
                {
                    this.ElementExpand.Expanded = this.Expanded;
                    this.ElementExpand.SetValue(Canvas.LeftProperty, this._padding.Left);
                    this.ElementExpand.SetValue(Canvas.TopProperty, this._padding.Top + 2.0);
                }
                if (this.ElementCheckbox != null)
                {
                    this.ElementCheckbox.SetValue(Canvas.TopProperty, 1.0);
                    this.ElementCheckbox.SetValue(Canvas.LeftProperty, num2);
                    this.ElementCheckbox.Width = 13.0;
                    this.ElementCheckbox.Height = 13.0;
                    this.ElementCheckbox.IsChecked = new bool?(this._checked);
                    num2 += this.ElementCheckbox.Width + 4.0;
                }
                this.ElementIcon.SetValue(Canvas.LeftProperty, num2);
                this.ElementIcon.SetValue(Canvas.TopProperty, this._padding.Top);
                this.UpdateNodeIcon();
                num2 += 20.0;
                base.ElementText.SetValue(Canvas.LeftProperty, num2);
                if (((this.Base != null) && (this.Base.MaxTextLength >= 0)) && (this._text.Length > this.Base.MaxTextLength))
                {
                    base.ElementText.Text = this._text.Substring(0, this.Base.MaxTextLength) + this.Base.TitlePostfix;
                }
                this.ElementSelectedText.SetValue(Canvas.LeftProperty, num2);
                base.ElementText.Text = this._text;
                this.ElementSelectedText.Text = this._text;
                this.ElementInput.Text = this._text;
                if (!this._editable)
                {
                    base.Width = (((double)base.ElementText.GetValue(Canvas.LeftProperty)) + base.ElementText.ActualWidth) + 5.0;
                    this.SetBackingSize(((double)base.ElementText.GetValue(Canvas.LeftProperty)) - 1.0, 0.0, base.ElementText.ActualWidth + 2.0, this.OverallHeight);
                    this.ElementBackground.Visibility = this._selected ? Visibility.Visible : Visibility.Collapsed;
                    base.ElementText.Visibility = this._selected ? Visibility.Collapsed : Visibility.Visible;
                    this.ElementSelectedText.Visibility = this._selected ? Visibility.Visible : Visibility.Collapsed;
                    this.ElementInput.Visibility = Visibility.Collapsed;
                }
                else
                {
                    this.ElementBackground.Visibility = Visibility.Collapsed;
                    base.ElementText.Visibility = Visibility.Collapsed;
                    this.ElementSelectedText.Visibility = Visibility.Collapsed;
                    this.ElementInput.Visibility = Visibility.Visible;
                }
                double num = ((double)base.ElementText.GetValue(Canvas.LeftProperty)) + base.ElementText.ActualWidth;
                this.ElementInput.Width = base.ElementText.ActualWidth + 10.0;
                if (base.Width < num)
                {
                    base.Width = num;
                }
            }
        }

        // Properties
        public bool AutoApplyCheckboxesToChildren { get; set; }


        public TreeView Base { get; set; }


        public bool Checked
        {
            get
            {
                return this._checked;
            }
            set
            {
                this._checked = value;
                if (this.ElementCheckbox != null)
                {
                    this.ElementCheckbox.IsChecked = new bool?(this._checked);
                }
                this.CheckChanged();
            }
        }

        public bool ChildrenLoaded { get; set; }


        public bool Editable
        {
            get
            {
                return this._editable;
            }
            set
            {
                TreeViewEventArgs args = new TreeViewEventArgs(base.ID);
                if ((this.Base != null) && this.Base.EnableNodeEditing)
                {
                    if (this._editable && (base.ElementRoot != null))
                    {
                        args.Source = this;
                        args.Target = this;
                        this.RaiseNodeEditingFinished(this, args);
                        if (!args.Cancel)
                        {
                            this._text = this.ElementInput.Text;
                        }
                    }
                    this._editable = value;
                    this.UpdateVisualState();
                    this.RecursiveUpdateSize();
                    this.RaiseNodeUpdateBase(this, EventArgs.Empty);
                }
            }
        }

        internal Rectangle ElementBackground { get; set; }


        internal TreeViewCheckBox ElementCheckbox { get; set; }


        internal TreeViewExpander ElementExpand { get; set; }


        internal Image ElementIcon { get; set; }


        internal TextBox ElementInput { get; set; }


        internal TextBlock ElementSelectedText { get; set; }


        public bool EnableCheckboxes
        {
            get
            {
                return this._enableCheckboxes;
            }
            set
            {
                this._enableCheckboxes = value;
                this.UpdateCheckbox();
                if (this.AutoApplyCheckboxesToChildren)
                {
                    foreach (TreeViewNode node in this.TreeViewNodes)
                    {
                        node.EnableCheckboxes = value;
                    }
                }
            }
        }

        public bool Expanded { get; set; }


        public bool HasChildren
        {
            get
            {
                if (this.TreeViewNodes.Count <= 0)
                {
                    return this._hasChildren;
                }
                return true;
            }
            set
            {
                this._hasChildren = value;
                if (!this._hasChildren)
                {
                    this.ChildrenLoaded = false;
                }
                this.UpdateVisualState();
            }
        }

        public string Icon
        {
            get
            {
                return this._icon;
            }
            set
            {
                this._icon = value;
                this.UpdateVisualState();
            }
        }

        public string IconExpanded
        {
            get
            {
                return this._iconExpanded;
            }
            set
            {
                this._iconExpanded = value;
            }
        }

        public bool IsContainer { get; set; }


        public new Thickness Margin { get; set; }


        public ObservableCollection<TreeViewNode> TreeViewNodes { get; set; }


        public double OverallHeight { get; set; }


        public new Thickness Padding
        {
            get
            {
                return this._padding;
            }
            set
            {
                this._padding = value;
                this.UpdateVisualState();
            }
        }

        public TreeViewNode ParentNode
        {
            get
            {
                return this._parent;
            }
            set
            {
                this._parent = value;
                this.ParentChanged();
            }
        }

        public bool Selected
        {
            get
            {
                return this._selected;
            }
            set
            {
                this._selected = value;
                this.UpdateVisualState();
            }
        }

        public string Text
        {
            get
            {
                return this._text;
            }
            set
            {
                this._text = value;
                this.UpdateVisualState();
            }
        }

        public object ToolTip
        {
            get
            {
                return ToolTipService.GetToolTip(this);
            }
            set
            {
                ToolTipService.SetToolTip(this, value);
            }
        }
    }


}
